use std::{rc::Rc, cell::RefCell, fmt, sync::{Arc, Mutex}};
use super::{local_vars::LocalVars, OperandStack, Thread, heap::{self, Method, Class}};

// 栈帧
#[derive(Clone)]
pub struct Frame {
    lower: Option<Rc<RefCell<Frame>>>, // stack is implemented as linked list
    local_vars: Option<Rc<RefCell<LocalVars>>>,
    operand_stack: Option<Rc<RefCell<OperandStack>>>,
    thread: Arc<Mutex<Thread>>,
    method: Arc<heap::Method>,
    next_pc: usize, // the next instruction after the call
}

// 实现send trait
unsafe impl Send for Frame {}

impl Frame {
    pub fn new_frame(thread: Arc<Mutex<Thread>>, method: Arc<heap::Method>) -> Frame {
        let local_vars = LocalVars::new_local_vars(method.get_max_locals());
        let operand_stack = OperandStack::new_operand_stack(method.get_max_stack());
        Frame { 
            lower: None, 
            local_vars: Some(Rc::new(RefCell::new(local_vars))), 
            operand_stack: Some(Rc::new(RefCell::new(operand_stack))),
            thread,
            method,
            next_pc: 0,
        }
    }

    pub fn new_frame1(thread: Arc<Mutex<Thread>>, method: Arc<heap::Method>, vars: LocalVars) -> Frame {
        let operand_stack = OperandStack::new_operand_stack(method.get_max_stack());
        Frame { 
            lower: None, 
            local_vars: Some(Rc::new(RefCell::new(vars))), 
            operand_stack: Some(Rc::new(RefCell::new(operand_stack))),
            thread,
            method,
            next_pc: 0,
        }
    }

    pub fn set_lower(&mut self, frame: Option<Rc<RefCell<Frame>>>) {
        self.lower = frame;
    }

    pub fn get_lower(&self) -> Option<Rc<RefCell<Frame>>> {
        self.lower.clone()
    }

    pub fn lower(&mut self) -> Option<Rc<RefCell<Frame>>> {
        self.lower.take()
    }

    pub fn get_next_pc(&self) -> usize {
        self.next_pc
    }

    pub fn set_next_pc(&mut self, pc: usize) {
        self.next_pc = pc;
    }

    pub fn get_local_vars(& self) -> Rc<RefCell<LocalVars>> {
        self.local_vars.as_ref().unwrap().clone()
    }

    pub fn get_operand_stack(&self) -> Rc<RefCell<OperandStack>> {
        self.operand_stack.as_ref().unwrap().clone()
    }

    pub fn get_thread(&self) -> Arc<Mutex<Thread>> {
        self.thread.clone()
    }

    pub fn get_method(&self) -> Arc<heap::Method> {
        self.method.clone()
    }

    pub fn revert_next_pc(&mut self) {
        self.next_pc = self.thread.lock().unwrap().get_pc() as usize;
    }
}

impl Frame {
    pub fn new_shim_frame(thread: Arc<Mutex<Thread>>, ops: Rc<RefCell<OperandStack>>) -> Frame {
        Frame { 
            lower: None, 
            local_vars: None, 
            operand_stack: Some(ops),
            thread: thread, 
            method: Arc::new(Method::shim_return_method()), 
            next_pc: 0 
        }
    }
    
    pub fn new_shim_frame_specify_class(thread: Arc<Mutex<Thread>>, ops: Rc<RefCell<OperandStack>>, class: Arc<Class>) -> Frame {
        Frame { 
            lower: None, 
            local_vars: Some(Rc::new(RefCell::new(LocalVars::new_local_vars(0)))), 
            operand_stack: Some(ops),
            thread: thread, 
            method: Arc::new(Method::shim_return_method_specify_class(class)), 
            next_pc: 0 
        }
    }
}

impl fmt::Display for Frame {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let method = self.get_method();
        let class_name = method.get_class().get_name();
        let line_num = method.get_line_number(self.get_next_pc() as i32);
        let mut s = format!(">> line:{} pc:{} {}.{}{} ", line_num, self.get_next_pc(), class_name, method.get_name(), method.get_descriptor());
        s += format!("\n   local_vars: {}", self.get_local_vars().borrow()).as_str();
        s += format!("\n   operand_stack: {}", self.get_operand_stack().borrow()).as_str();
        write!(f, "{}", s)
    }
}